package icasue.tools.sqlhelper.adaptor.ibatis;

import icasue.reflect.annotations.NotNull;
import icasue.reflect.handles.classes.ClassOF;
import icasue.reflect.handles.predicate.SureOF;
import icasue.tools.sqlhelper.adaptor.annotation.AutoProviderMetadata;
import icasue.tools.sqlhelper.common.Const;
import icasue.tools.sqlhelper.core.StandardSqlProvider;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.SQLException;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2021/11/3 下午5:24
 * @UpdateDate:
 * @Description:
 */
public final class ReflectionSqlProvider extends StandardSqlProvider<Object, Annotation> {

    private static ThreadLocal<AutoProviderMetadata> metadataStore;
    private Method $mappingMethod = null;

    static {
        metadataStore = new ThreadLocal<>();
    }

    public ReflectionSqlProvider() throws SQLException {
        this(metadataStore.get());
    }

    public ReflectionSqlProvider(@NotNull AutoProviderMetadata metadata) throws SQLException {
        super((Class<Object>) metadata.javaType(),(Class<Annotation>) metadata.colMark());
    }

    @Override
    protected String database() {
        return metadataStore.get().database();
    }

    @Override
    protected String table() {
        return metadataStore.get().table();
    }

    @Override
    protected boolean recursionDiscoveryFields() {
        return metadataStore.get().recursionDiscoveryFields();
    }

    @Override
    protected String mappingOnMarkAnnotation(Field jField, Annotation markAnnotation) {
        tryInit();
        String column = Const.$EMPTY;
        try {
            column =  (String)$mappingMethod.invoke(markAnnotation, null);
        }catch (Throwable e){
            /*Here will can't be execute anytime after init()*/
        }
        return column;
    }

    private void tryInit() {
        if($mappingMethod != null){
            return;
        }
        if(!metadataStore.get().colMark().equals(Annotation.class)){
            if(!metadataStore.get().mappingMethod().equals(Const.$EMPTY)){
                try {
                    $mappingMethod = ClassOF.getDeclaredMethod_.apply(
                            metadataStore.get().colMark(), metadataStore.get().mappingMethod(), null);
                    SureOF.equals_.accept($mappingMethod.getReturnType(), String.class);
                }catch (Throwable e){
                    throw new RuntimeException("AutoProviderMetadata's declared mappingMethod is inValid.");
                }
            }else {
                throw new RuntimeException("AutoProviderMetadata's declared mappingMethod for colMark is necessary.");
            }
        }
    }
}
